﻿#region Using

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.ServiceModel;

using Common.Base;

using CommunicationLibrary;
using CommunicationLibrary.TransportData;
using CommunicationLibrary.TransportData.CommandArgs;
using CommunicationLibrary.TransportData.Enums;

using GameNetwork.Interfaces;

#endregion

namespace GameNetwork.Network
{
    internal class NetworkManager : IMenuNetwork, IGameNetwork
    {
        private const string Server = "net.tcp://link.imn.htwk-leipzig.de:1338/Server";

        private readonly INetworkClient networkClient;

        private int playerId;

        private IServer channel;

        public NetworkManager()
        {
            this.networkClient = new NetworkClient();
            this.networkClient.OnGameStarted += () => this.IsGameStarted = true;
            this.networkClient.OnGameReady += this.GameReady;
            this.networkClient.OnGameWon += this.GameWon;
            this.networkClient.OnGameLost += this.GameLost;
            this.networkClient.OnConnectionError += this.ConnectionError;
        }

        public event GameLostEventHandler OnGameLost;

        public event ConnectionErrorEventHandler OnConnectionError;

        public event GameWonEventHandler OnGameWon;

        public event GameLostByOtherPlayerEventHandler OnGameLostByOtherPlayer;

        public bool IsGameStarted { get; private set; }

        public bool IsGameReadyToLoad { get; private set; }

        public Player Player { get; private set; }

        public IEnumerable<Player> Opponents { get; private set; }

        public void SetupConnection()
        {
            this.networkClient.ClearGameStates();

            try
            {
                this.channel = DuplexChannelFactory<IServer>.CreateChannel(this.networkClient.GetClient(), FarmersBreakfastTcpBinding.Instance, new EndpointAddress(Server));
            }
            catch (Exception)
            {
                this.ConnectionError();
            }
        }

        public void CancelRegistration()
        {
            try
            {
                this.channel.CancelRegistration();
            }
            catch (Exception)
            {
            }

            this.networkClient.ClearGameStates();
            this.channel = null;
            this.IsGameStarted = false;
            this.IsGameReadyToLoad = false;
        }

        public void QuitGame()
        {
            try
            {
                this.channel.QuitGame();
            }
            catch (Exception)
            {
            }

            this.networkClient.ClearGameStates();
            this.channel = null;
            this.IsGameStarted = false;
            this.IsGameReadyToLoad = false;
        }

        public GameState GetGameState()
        {
            var state = this.networkClient.GetCurrentGameState();

            this.Player = state.Players.First(x => x.Identifier == this.playerId);
            this.Opponents = state.Players.Where(x => x.Identifier != this.playerId);

            return state;
        }

        public Result NewRoute(RouteTypeEnum routeType, Position start, Position end)
        {
            try
            {
                if (routeType == RouteTypeEnum.Farmer)
                {
                    Debug.WriteLine("NetworkManager: Add Farmer Route: {0}/{1} to {2}/{3}", start.X, start.Y, end.X, end.Y);
                    var result = this.channel.SendFarmerRouteAddCommand(new RouteCommandArgs(start, end));
                    Debug.WriteLine("NetworkManager: Result " + result.Message);
                    return result;
                }
                else
                {
                    Debug.WriteLine("NetworkManager: Add Miltia Route: {0}/{1} to {2}/{3}", start.X, start.Y, end.X, end.Y);
                    var result = this.channel.SendMilitiaRouteAddCommand(new RouteCommandArgs(start, end));
                    Debug.WriteLine("NetworkManager: Result " + result.Message);
                    return result;
                }
            }
            catch (Exception)
            {
                this.ConnectionError();
            }

            return null;
        }

        public Result RemoveRoute(RouteTypeEnum routeType, Position start, Position end)
        {
            try
            {
                if (routeType == RouteTypeEnum.Farmer)
                {
                    Debug.WriteLine("NetworkManager: Remove Farmer Route: {0}/{1} to {2}/{3}", start.X, start.Y, end.X, end.Y);
                    var result = this.channel.SendFarmerRouteRemoveCommand(new RouteCommandArgs(start, end));
                    Debug.WriteLine("NetworkManager: Result " + result.Message);
                    return result;
                }
                else
                {
                    Debug.WriteLine("NetworkManager: Remove Miltia Route: {0}/{1} to {2}/{3}", start.X, start.Y, end.X, end.Y);
                    var result = this.channel.SendMilitiaRouteRemoveCommand(new RouteCommandArgs(start, end));
                    Debug.WriteLine("NetworkManager: Result " + result.Message);
                    return result;
                }
            }
            catch (Exception)
            {
                this.ConnectionError();
            }

            return null;
        }

        public Result BuildGoldMine(Position pos)
        {
            try
            {
                Debug.WriteLine("NetworkManager: Add GoldMine {0}/{1}", pos.X, pos.Y);
                var args = new BuildCommandArgs(pos, BuildingTypeEnum.GoldMine);
                var result = this.channel.SendBuildCommand(args);
                Debug.WriteLine("NetworkManager: Result " + result.Message);

                return result;
            }
            catch (Exception)
            {
                this.ConnectionError();
                return null;
            }
        }

        public Result BuildBarrack(Position pos)
        {
            try
            {
                Debug.WriteLine("NetworkManager: Add Barrack {0}/{1}", pos.X, pos.Y);
                var args = new BuildCommandArgs(pos, BuildingTypeEnum.Barrack);
                var result = this.channel.SendBuildCommand(args);
                Debug.WriteLine("NetworkManager: Result " + result.Message);

                return result;
            }
            catch (Exception)
            {
                this.ConnectionError();
                return null;
            }
        }

        public bool Register(string player)
        {
            if (this.channel == null)
            {
                return false;
            }

            try
            {
                var result = this.channel.RegisterForMatch(player);
                return result.ResultType == ResultTypeEnum.Success;
            }
            catch (Exception)
            {
                this.ConnectionError();
                return false;
            }
        }

        public bool Ready()
        {
            if (this.channel == null)
            {
                return false;
            }

            try
            {
                var result = this.channel.ReadyForGameStart();
                return result.ResultType == ResultTypeEnum.Success;
            }
            catch (Exception)
            {
                this.ConnectionError();
                return false;
            }
        }

        private void GameReady(GameState state, int id)
        {
            this.IsGameReadyToLoad = true;
            this.playerId = id;
            this.Player = state.Players.First(x => x.Identifier == id);
            this.Opponents = state.Players.Where(x => x.Identifier != id);
        }

        private void GameLost(int id)
        {
            if (this.Player == null || !this.Opponents.Any())
            {
                this.ConnectionError();
                return;
            }

            if (this.Player.Identifier == id)
            {
                if (this.OnGameLost != null)
                {
                    this.OnGameLost(this.Player);
                }

                return;
            }

            var opponent = this.Opponents.FirstOrDefault(x => x.Identifier == id);
            if (opponent != null && this.OnGameLostByOtherPlayer != null)
            {
                this.OnGameLostByOtherPlayer(opponent);
            }
        }

        private void GameWon()
        {
            if (this.OnGameWon != null)
            {
                this.OnGameWon(this.Player);
            }
        }

        private void ConnectionError()
        {
            if (this.OnConnectionError != null)
            {
                this.OnConnectionError();
            }
        }
    }
}